package com.hzlj.position.locate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fz.common.base.core.CollectionUtils;
import com.fz.common.base.core.db.function.ColumnFormatUtil;
import com.fz.common.base.core.db.function.GroupConcatUtil;
import com.fz.common.base.core.db.function.IFNullUtil;
import com.fz.common.base.dto.PageParamDTO;
import com.fz.common.base.dto.QueryPageResultDTO;
import com.fz.common.dict.annotation.DictConvert;
import com.google.common.collect.Lists;
import com.hzlj.position.config.common.enums.ChannelType;
import com.hzlj.position.locate.cache.PositionNewestCache;
import com.hzlj.position.locate.common.dto.position.PositionHandleDTO;
import com.hzlj.position.locate.common.dto.positionNewest.PositionNewestQueryParamDTO;
import com.hzlj.position.locate.common.dto.positionNewest.PositionNewestQueryResultDTO;
import com.hzlj.position.locate.common.dto.positionNewest.PositionNewestQuerySimpleResultDTO;
import com.hzlj.position.locate.common.model.Position;
import com.hzlj.position.locate.common.model.PositionNewest;
import com.hzlj.position.locate.dao.PositionNewestDao;
import com.hzlj.position.locate.mapper.PositionNewestMapper;
import com.hzlj.position.locate.service.PositionNewestService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.stereotype.Service;

import javax.annotation.Nonnull;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 定位记录-分表(PositionNewest)表服务接口
 *
 * @author lifh
 * @date 2023-03-16 21:48:43
 */
@Slf4j
@Service
public class PositionNewestServiceImpl implements PositionNewestService {

    //locate:newest:time:jzId:deviceCode
    private final static String NEW_POSITION_TIME_CACHE = "locate:newest:time:%s:%s";
    @Resource
    private PositionNewestDao positionNewestDao;
    @Resource
    private PositionNewestMapper positionNewestMapper;
    @Resource
    private RedisTemplate<String, Date> redisTemplate;
    @Resource
    private PositionNewestCache positionNewestCache;


    @Override
    public PositionNewestQueryResultDTO getPositionNewestByJzId(String jzId) {
        return this.positionNewestDao.getPositionNewestByJzId(jzId);
    }


    @Override
    public void saveBatch(List<Position> positions) {
        if (ObjectUtil.isEmpty(positions)) {
            return;
        }
        //去重
        List<PositionNewest> adds = positions.stream().map(e -> BeanUtil.copyProperties(e, PositionNewest.class))
                .filter(CollectionUtils.distinctByKey(e -> e.getDeviceCode() + ":" + e.getChannel()))
                .collect(Collectors.toList());
        //拼接SQL(为了效率问题)
        String removeKeys = adds.stream().map(e -> "('" + e.getDeviceCode() + "','" + e.getChannel() + "')").collect(Collectors.joining(","));
        positionNewestMapper.removeKeys(removeKeys);

        //新增
        positionNewestDao.saveBatch(adds);

        //保存最新的定位时间
        this.saveNewestPositionTimes(positions);
    }

    @Override
    public void handle(PositionHandleDTO dto) {
        this.positionNewestDao.handle(dto);
    }

    @Override
    public void handleSysCheck(PositionHandleDTO dto) {
        this.positionNewestDao.handleSysCheck(dto);
    }

    @Override
    public Date getPositionTime(String jzId, String deviceCode) {
        String key = String.format(NEW_POSITION_TIME_CACHE, jzId, deviceCode);
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    @DictConvert
    public List<PositionNewestQuerySimpleResultDTO> getPositionNewestByJzIds(List<String> jzIds) {
        List<String> maxIdsByJzIds = positionNewestDao.getMaxIdsByJzIds(jzIds);
        if (ObjectUtil.isEmpty(maxIdsByJzIds)) {
            return Lists.newArrayList();
        }
        return positionNewestDao.getPositionNewestByIds(maxIdsByJzIds);
    }

    /**
     * 查询列表：不分页
     *
     * @param dto 查询条件
     * @return 对象列表
     */
    @Override
    @DictConvert
    public List<PositionNewestQueryResultDTO> listPositionNewest(PositionNewestQueryParamDTO dto) {
//        this.initQueryParams(dto);
//        return positionNewestMapper.list(dto);
        return positionNewestCache.positionNewest(dto);
    }

    /**
     * 查询列表：分页
     *
     * @param dto  查询条件
     * @param page 分页信息
     * @return 对象列表
     */
    @Override
    @DictConvert
    public QueryPageResultDTO<PositionNewestQueryResultDTO> pagePositionNewest(PositionNewestQueryParamDTO dto, PageParamDTO page) {
//        this.initQueryParams(dto);
//        return QueryWrapperConverter.fromPage(this.positionNewestMapper.page(
//                        new Page<>(page.getPage(), page.getSize()), dto
//                )
//        );
        List<PositionNewestQueryResultDTO> positions = positionNewestCache.positionNewest(dto);
        return new QueryPageResultDTO<>(positions, page.getPage(), page.getSize());
    }

    /**
     * 保存最新的定位时间(只保存运营商+腕带的)
     */
    @SuppressWarnings("unchecked")
    private void saveNewestPositionTimes(List<Position> positions) {
        List<Position> newestPositionTimes = positions.stream()
                .filter(e -> ChannelType.MOBILE.getCode().equals(e.getChannelType())
                        || ChannelType.WD.getCode().equals(e.getChannelType()))
                .collect(Collectors.toList());
        //批量保存
        redisTemplate.executePipelined(new SessionCallback<Object>() {
            @Override
            public Object execute(@Nonnull RedisOperations operations) throws DataAccessException {
                for (Position position : newestPositionTimes) {
                    String key = String.format(NEW_POSITION_TIME_CACHE, position.getJzId(), position.getDeviceCode());
                    operations.opsForValue().set(key, position.getPositionTime(), 1, TimeUnit.DAYS);
                }
                return null;
            }
        });
    }


    private void initQueryParams(PositionNewestQueryParamDTO params) {
        params.setDeviceCodeGroupSql(GroupConcatUtil.format("device_code", "channel", "/"))
                .setChannelTypeGroupSql(GroupConcatUtil.format("channel_type", "channel", "/"))
                .setChannelGroupSql(GroupConcatUtil.format("channel", "channel", "/"))
                .setSortSql(IFNullUtil.format("c.id", "0"))
                .setPositionFormat(ColumnFormatUtil.format("position"));
    }
}
